JavaScript 数组Array

文章为《JavaScript高级程序设计》的讀書笔记.
涉及:Array类型的创建; 读取及设置; 检测方法; 转换方法; 栈方法; 队列方法; 重排序方法; 操作方法 位置方法;迭代及归并方法等,之前对JS数组的相关知识理解不够深入,特做一个总结,和大家一起学习,相信看完文章后你会对JS数组有个更加全面和深入的认识.

Array创建方式

1. 使用Array构造函数

1
var colors = new Array();

若给构造函数传递数量,如:

1
var colors = new Array(10);

该数量会自动变成数组length属性的值。

也可以像Array构造函数传递数组中应该包含的项。如:

1
2
//创建一个包含3项的数组,值分别为字符串"red","green","blue"
var colors = new Array("red","green","blue");

注:使用Array构造函数时也可以省略new操作符,如下两种方式结果相同。

1
2
var colors = Array(3);		//创建一个包含3项的数组
var colors = Array("red"); //创建一个包含1项,即字符串"red"的数组

2. 使用数组字面量表示法

数组字面量由一对包含数组项的方括号表示,多个数组之间用逗号,隔开

1
2
3
4
var colors = ["red","green","blue"];//创建一个包含3个字符串的数组
var students = []; //创建一个空数组
var nums = [1,2,]; //创建一个包3(IE:包含3项1,2,undefined的数组)或含2(其他浏览器)项的数组
var options = [,,,,,]; //创建一个包含6(IE8之前)或5项(IE9+及其他浏览器)的数组

注: 与对象一样,在使用数组字面量表示法时,也不会调用Array构造函数(Firefox3及更早的版本除外)


读取和设置Array

在读取和设置数组的值时,要使用方括号并提供相应值的基于0的数组索引,如下:

1
2
3
4
var colors = ["red","green","blue"];//创建一个包含3项的字符串数组
alert(colors[0]); //显示第一项,"red"
colors[2] = "black"; //修改第三项
colors[3] = "gray"; //注:此处为新增第四项

如果方括号中索引小于数组的项数,则返回对应项的值;设置数组使用相同的方法,但会替换指定位置的值.

注: 如果设置某个值的索引超过了数组的现有项数,如上colors[3]所示,数组会自动增加到该索引值加一的长度.

数组的length属性不是只读的,通过设置这个属性,可以从数组的末尾移除项或者添加新项.

1
2
3
var colors = ["red","green","blue"];    //创建一个包含3个字符串的数组
colors.length = 2; //设置数组的长度为2
alert(colors[2]); //undefined

如上开始常见一个含有3项的数组,后将数组的length设置为2,这样会移除数组的最后一项,结果再访问colors[2]就会显示undefined。
如果把length属性设置为大于数组项数的值,则新增的每一项都会取的undefined值。如下:

1
2
3
var colors = ["red","green","blue"];    //创建一个包含3个字符串的数组
colors.length = 5; //设置数组的长度为5
alert(colors[3]); //undefined

※ 利用length属性可以方便的在数组末尾添加新项,如下:

1
2
3
var colors = ["red","green","blue"];    //创建一个包含3个字符串的数组
colors[colors.length] = "gray"; //在位置【3】添加gray色
colors[colors.length] = "white"; //在位置【4】添加white色

由于数组的最后一项的索引时钟是length-1,因此下一个新项的索引就是length.

注: 当把一个一个值放在超出数组大小的位置上时,数组就会重新计算其长度值,即长度值等于最后一项的索引加1,如下:

1
2
3
var colors = ["red","green","blue"];    //创建一个包含3个字符串的数组
colors[99] = "black"; //在位置99处创建一种颜色
alert[colors.length]; //100

检测数组

TIPS : instanceof Array.isArray()
对于一个网页,或者一个全局作用域而言,使用instanceof就可以确定某个对象是不是数组.如下:

1
2
3
if(values instanceof Array){
//对数组的某些操作
}

但也有一些问题:如果网页中包含多个框架,实际上就是有两个或以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数.如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中的原生创建的数组分别具有各自不同的构造函数.
为此ECMAScrip5新增了Array.isArray()方法来确定一个值是不是一个数组,不管它在那个全局执行环境中创建.用法如下:

1
2
3
if (Array.isArray(value)){
//对数组执行某些操作
}

注意: typeof不能判断是否为Array类型, 它只适合基本数据类型,undefined,及函数类型判断,遇null失效,数组类型返回"object".

1
2
3
4
5
6
7
alert(typeof 100);              //"number"
alert(typeof true); //"boolean"
alert(typeof (function(){})); //"function"
alert(typeof undefined); //"undefined"
alert(typeof (new Object())); //"object"
alert(typeof ([1,2])); //"object"
alert(typeof NaN); //"number"

Object.prototype.toString也可以判断数组

1
Object.prototype.toString.apply([]);    //"[object Array]";

更多关于数据类型监测请查看JavaScript 数据类型监测


转换方法

TIPS: toLocaleString() toString() valueOf() join()

toString() 方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串.
valueOf() 方法返回的还是数组
toLocaleString()经常返回和toString() valueOf() 一样的值.不同的是:为了取得每一项的值,
调用的是每一项toLocaleString()方法,而不是toString()方法.

1
2
3
4
5
var colors = ["red","green","blue"];
alert(colors.toString()); //red,green,blue
alert(colors.valueOf()); //red,green,blue
alert(colors.toLocaleString()); //red,green,blue
alert(colors); //red,green,blue

最后一行直接alert(),它会在后台调用toString()方法.

以上三种方法默认以逗号分隔字符串的形式返回数组项.如果使用join()方法,则可以以不同的分隔符来构建字符串.如下:

1
2
3
var colors = ["red","green","blue"];
colors.join(","); //red,green,blue 以,分隔数组项
colors.join("||"); //red||green||blue 以||分隔数组项


栈方法

TIPS: push() pop()

: 可以限制插入和删除的数据结构,是一种LIFO(Last-In-First-Out 后进先出)的数据结构.最新添加的项最早被移除.栈中项的插入(推入)和移除(弹出),只发生在一个位置-栈的顶部.

ECMAScrip 数组提供了一种让数组的行为类似于其他数据结构的方法,数组可以表现的像栈一样,数组专门提供了push()pop()方法,以便实现类似栈的行为.

push(): 可以接受任意数量的参数,把他们逐个添加到数组末尾,并返回修改后数组的长度.
pop(): 从数组的末尾移除最后一项,减少数组的length值,返回移除的项.
如下:

1
2
3
4
5
6
7
var colors = ["red"];
var count = colors.push("green","blue"); //推入两项
alert(count); //3 push返回推入后数组长度

var item = colors.pop(); //移除最后一项并返回该项
alert(item); //blue
alert(colors.length); //2

可以将栈方法与其他数组方法连用,可以实现不凡的效果,像下面这样:

1
2
3
4
5
6
7
var colors = ["red","green"];
colors.push("blue"); //数组末尾添加"blue"
colors[3] = "black"; //添加一项
alert(colors.length); //4

var item = colors.pop(); //移除末尾最后一项并返回
alert(item); //black


队列方法

TIPS:

队列: 访问规则是FIFO(First-In-First-Out)先进先出, 队列在列表的末尾添加项, 从列表的前端移除项.

push()方法是向数组的末端添加项,要模拟队列只需从数组前端取出项的方法,实现这一操作的数组方法是shift(), 同时还提供了unshift()方法.

shift(): 移除数组中的第一项并返回该项,同时将数组的长度减1.
unshift(): 在数组的前端添加任意项并返回新数组的长度.

shift()push()方法组合, 可以像队列一样使用数组.在数组的末尾添加项,前端移除项.

1
2
3
4
5
6
7
8
9
10
var colors = new Array();
var count = colors.push("red","green"); //推入两项
alert(count); //2

count = colors.push("blue"); //推入第三项
alert(count); //3

var item = colors.shift(); //移除前端第一项并返回
alert(item); //red
alert(colors.length); //2

unshift()pop()方法组合,可以从相反的方向来模拟队列.即在数组的前端添加项,从数组的末尾移除项.

1
2
3
4
5
6
7
8
9
10
11
var colors = new Array();
var count = colors.unshift("red","green"); //从前端推入两项
alert(count); //2

count = colors.unshift("black"); //从前端推入另一项
alert(count); //3
alert(colors); //black,red,green

var item = colors.pop(); //移除末尾最后一项并返回
alert(item); //green
alert(colors.length); //2


重排序方法

TIPS : reverse() sort()

reverse( ): 反转数组项顺序

1
2
3
var values = [1,2,3,4,5,];
values.reverse();
alert(values); //5,4,3,2,1

sort( ): 按升序排列数组项,默认调用每个数组项的toString()方法,比较得到的字符串.

注意: 它比较的是字符串,即便每一项都是数值也是如此.

1
2
3
var values = [1,2,12,8,26,5];
values.sort();
alert(values); //1,12,2,26,5,8

因这种排序方式不是最佳方案,sort()方法可以接受一个比较函数作为参数,比较数组项的前后顺序.

  • 如果第一个值应该位于第二个值之前则返回一个负数;
  • 如果两个参数相等,则返回0;
  • 如果第一个参数位于第二个参数之后则返回一个整数;
1
2
3
4
5
6
7
8
9
10
11
12
function compare(a,b){
if(a < b){
return -1;
}else if(a > b){
return 1;
}else{
return 0;
}
}
var values = [1,2,12,8,26,5];
values.sort(compare);
alert(values); //1,2,5,8,12,26

比较函数可以适用于大多数数据类型,通过交换比较函数返回值, 可以产生降序的结果.

对于数值类型或者valueOf()方法会返回数值类型的对象类型,可以用更简便的比较函数.

1
2
3
4
function compare(a,b){
return a - b; //升序
//return b - a; //降序
}

操作方法

TIPS: concat() slice() splice()
ECMAScript为操作已经包含在数组中的项,提供了如下方法:

concat(): 基于当前数组中的所有项创建一个新数组.

  • 没有参数时,只是复制当前数组并返回副本;
  • 参数时一个或多个数组,则将这些数组每一项都添加到结果数组中;
  • 若传递的值不是数组,这些值简单的添加到结果数组的末尾.
1
2
3
4
5
6
7
8
9
var colors = ["red","blue"];
var colors1 = colors.concat();
var colors2 = colors.concat(["green"],["black","white"]);
var colors3 = colors.concat("gray");

alert(colors); //red,blue 注意: 原数组不变
alert(colors1); //red,blue concat()没有参数时,只是复制原数组
alert(colors2); //red,blue,green,black,white 把传递过来的多个数组添加至原数组末尾
alert(colors3); //red,blue,gray 当传递过来的不是数组时,也会被加添到末尾.

slice(): 基于当前数组中的一个或多个项创建一个新数组.

  • 带一个参数时,返回从该参数指定位置开始到当前数组末尾的所有项.
  • 带两个参数,返回起始和结束位置之间的项,不包括结束位置的项.

    注意:concat() 方法和slice() 返回的都是新数组,对原数组不会有影响.

1
2
3
4
5
6
7
var colors = ["red","blue","green","black","white"];
var colors1 = colors.slice(2);
var colors2 = colors.slice(1,3);

alert(colors); //red,blue,green,black,white
alert(colors1); //green,black,white 返回从位置colors[2]开始到数组末尾的项
alert(colors2); //blue,green 返回colors[1]到colors[3]之间的项,但不包括colors[3]="black"

注意: 如果slice()方法的参数中有负数, 则用数组长度加上该数来确定相应的位置,如:以上数组包含5项,调用slice(-3,-1)与slice(2,4)效果相同.

1
alert(colors.slice(-3,-1)); //green black

若结束位置小于其实位置,则返回空数组.

splice():这是最强大的数组方法!主要用途是向数组中部插入向.使用方法有一下三种

  • 删除: 删除任意数量的项.2个参数: 要删除的第一项的位置和要删除的项数.
    例:splice(0,2) 会删除数组中的前两项.

  • 插入:向指定位置插入任意数量的项.3个参数:起始位置;0(要删除的项数);要插入的项,如果要插入更多的项,依次添加到第四第五以至更多的的参数.
    例: splice(1,0,"red","black"),会从当前数组位置1开始插入字符串"red","black".

  • 替换:向指定位置插入任意数量的项,同时删除任意数量的项.3个参数:起始位置; 删除的项数; 要插入的任意数量的项.插入的项数不必与删除的项数相等.
    例:splice(2,1,"orange","rose"),会删除数组位置为2的项并从位置2开始插入"orange","rose".
1
2
3
4
5
6
7
8
9
10
11
12
13
var colors = ["gray","white","yellow","gold"];
var removed = colors.splice(0,2); //从0位置开始删除两项

alert(colors); //yellow,gold
alert(removed); //gray,white 返回删除的项

removed = colors.splice(1,0,"red","black"); //从1位置开始删除0项插入两项
alert(colors); //yellow,red,black,gold
alert(removed); //空数组

removed = colors.splice(2,1,"orange","rose"); //从位置2开始删除1项,插入两项
alert(colors); //yellow,red,orange,rose,gold
alert(removed); //black 返回删除的项

splice()方法始终都会有一个返回的数组,该数组中包含从原始数组中删除的项(若没有删除任何项,则返回空数组).


位置方法

TIPS: indexOf() lastIndexOf()

indexOf()lastIndexOf()都接受两个参数:要查找的项和表示起点位置的索引(可选,没有的话默认从数组开头查到末尾).
indexOf()方法从数组的开头(位置0)开始向后查找,lastIndexOf()从数组的末尾开始向前查找.
两个方法都返回要查找的项在数组中的位置,如果没有找到则返回-1;比较第一个参数与数组中每一项时,会使用全等===操作符,必须要查找的项和第一个参数是严格相等.例如下:

1
2
3
4
5
6
7
var nums = [5,6,3,9,7,3,8];
alert(nums.indexOf(3)); //2
alert(nums.lastIndexOf(3)); //5

alert(nums.indexOf(9,2)); //3
alert(nums.lastIndexOf(9,2)); //-1 因为是从位置2往前查询,位置2之前已经没有9,所以返回-1
alert(nums.lastIndexOf(9,5)); //3

同样可以用来查找对象等其他类型.

1
2
3
4
5
6
7
var student = {name: "tom"};
var person = [{name: "tome"}];

var morePerson = [student];

alert(person.indexOf(student)); //-1
alert(morePerson.indexOf(student)); //0


迭代方法

TIPS: every() filter() forEach() map() some()

ECMAScript定义了5个迭代方法.每个方法接受两个参数:要在每一项上运行的函数;运行该函数的作用域对象-影响this的值(可选参数).
传入运行函数都会接受三个参数:数组项的值;该项在数组中的位置;数组对象本身.
5个方法都不会修改原数组中的包含的值.

  • every(): 对数组中每一项运行给定函数,如果该函数对每一项都返回true,则放回true.
  • filter(): 对数组中每一项运行给定函数,返回该函数会返回true的项组成的数组.
  • forEach(): 对数组中每一项运行给定函数,没有返回值.
  • map(): 对数组中每一项运行给定函数, 返回每次函数调用的结果组成的数组.
  • some(): 对数组中每一项运行给定函数, 如果该函数对任一项返回true,则返回true.

every()some()比较相似.
every()方法:传入的函数必须对每一项都返回true,这个方法才返回true;
some()方法:只要传入的函数对数组中的某一项返回true,则some()方法返回true.
例如下:

1
2
3
4
5
6
7
8
9
10
11
12
var nums = [2,4,6,3,15,9];
var everyResult = nums.every(function(item,index,arr){
return (item > 4);
});

alert(everyResult); //false

var someResult = nums.some(function(item,index,arr){
return (item > 4);
});

alert(someResult); //true

filter()利用指定的函数确定是否在返回的数组中包含某一项.例:要返回一个所有值都大于2的数组.

1
2
3
4
5
var nums = [2,4,6,3,15,9];
var filterResult = nums.filter(function(item,index,arr){
return (item > 4);
})

alert(filterResult); //6,15,9

这个方法对查询符合某些条件的所有数组项非常有用.
map() 返回一个数组,这个数组的每一项都是在原始数组中的对应项上运行传入函数的结果.例: 给数组的每一项乘2,返回这些乘积 组成的数组.

1
2
3
4
5
var nums = [2,3,4,5,6];
var mapResult = nums.map(function(item,index,arr){
return item * 2;
});
alert(mapResult); //4,6,8,10,12

这个方法适合创建包含项与另一个数组一一对应的数组.

forEach()只是对数组的每一项运行传入的函数,没有返回项,本质上与使用for循环迭代数组一样.

1
2
3
4
5
var nums = [1,2,4,3,5,];
nums.forEach(function(item,index,arr){
//执行数组相关操作
})

alert(nums);


归并方法

TIPS: reduce() reduceRight()

reduce() reduceRight()都会迭代数组的所有项,然后构建一个最终返回的值.reduce()方法从数组的第一项开始,reduceRight()从数组的最后一项开始.

两个方法都接受两个参数:

  • 在每一项上调用的函数.接受4个参数:前一个值;当前值;项的索引;数组对象.这个函数返回的任何值都会作为第一个参数自动传给下一项.
  • 作为归并基础的初始值(可选).
    例: 使用reduce()方法执行数组中所有值求和的操作:
1
2
3
4
5
6
7
8
9
var nums = [1,2,3,4,5,6];
var sum1 = nums.reduce(function(prev,cur,index, array){
return prev + cur;
});

var sum2 = nums.reduceRight(function(prev,cur,index, array){
return prev + cur;
});

alert(sum1); //21
alert(sum2); //21

reduceRight()方法执行效果与reduce()类似,只是方向相反,他们的区别在于从哪头开始遍历数组,除此之外,他们完全相同.